package com.rd.sys.service.analysis.result.impl;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;

import com.rd.sys.dto.client.store.*;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import baseproj.common.exception.BaseServiceException;

import com.rd.sys.common.base.CommonConstants;
import com.rd.sys.common.utils.JacksonMapper;
import com.rd.sys.common.utils.cache.AnalysisCache;
import com.rd.sys.common.utils.client.AnalysisPathUtils;
import com.rd.sys.common.utils.file.FileLockUtils;
import com.rd.sys.common.utils.file.HandlerCaller;
import com.rd.sys.dto.client.analysis.FixData;
import com.rd.sys.dto.client.analysis.SchemeResultData;
import com.rd.sys.dto.client.data.GazeData;
import com.rd.sys.dto.client.store.statistic.StatisticFixationData;
import com.rd.sys.dto.client.store.statistic.StatisticSaccadeData;
import com.rd.sys.dto.client.store.statistic.TempIndex;
import com.rd.sys.service.analysis.result.ResultService;
import com.rd.sys.service.analysis.scheme.SchemeService;
import com.rd.sys.service.analysis.statistic.StatisticService;

@Service
@Transactional(rollbackFor = {Exception.class})
public class ResultServiceImpl implements ResultService {
    protected static final Logger logger = LoggerFactory.getLogger(ResultServiceImpl.class);

    @Autowired
    private SchemeService schemeService;

    @Autowired
    private StatisticService statisticService;

    /**
     * 查询某个素材的原始数据
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param materialCode
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<SchemeResultData> queryResultRawDataByMaterial(String userCode, String projectCode, String schemeCode,
            String materialCode) throws Exception {
        List<SchemeResultData> list = null;
        try {
            /** 读取方案下所有被试信息 */
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;

            List<Result> resultList = AnalysisCache.ResultCache.get(key);

            if (resultList != null && resultList.size() > 0) {
                /** 读取方案下指定的素材信息 */
                Material material = schemeService.queryMaterial(userCode, projectCode, schemeCode, materialCode);

                /** 遍历所有被试,按素材时间过滤所需数据 */
                list = new LinkedList<SchemeResultData>();
                Iterator<Result> iterator = resultList.iterator();
                while (iterator.hasNext()) {
                    // 取出每个被试
                    Result result = iterator.next();
                    SchemeResultData resutlData = new SchemeResultData(result.getResultCode(), result.getResultName());
                    resutlData.setAnswers(result.getAnswers());

                    // 从缓存中读取该被试的所有数据
                    String key_data = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                            + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode
                            + CommonConstants.AnalysisConstats.CACHE_SPLIT + result.getResultCode();
                    List<Data> dataList = AnalysisCache.DataCache.get(key_data);

                    // 根据素材过滤数据
                    if (dataList != null && dataList.size() > 0) {
                        List<Data> rtnDataList = new LinkedList<Data>();
                        // 将第一条数据的时间作为基准时间
                        Data data = dataList.get(0);
                        long baseTime = data.getTime();
                        long startTime = baseTime + material.getOffset();
                        long endTime = startTime + material.getDuration();

                        // 判断数据是否落在该素材的时间范围内
                        Iterator<Data> iterator_data = dataList.iterator();
                        while (iterator_data.hasNext()) {
                            data = iterator_data.next();
                            if (data.getTime() >= startTime && data.getTime() < endTime) {
                                rtnDataList.add(data);
                            }
                        }
                        resutlData.setData(rtnDataList);
                    }

                    list.add(resutlData);
                }

            }

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query result data by material error", ex);
            throw new RuntimeException(ex);
        }
        return list;
    }

    /**
     * 查询某个素材的注视点数据
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param materialCode
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<SchemeResultData> queryResultFixationDataByMaterial(String userCode, String projectCode,
            String schemeCode, String materialCode) throws Exception {
        List<SchemeResultData> list = null;
        try {
            /** 读取方案下所有被试信息 */
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;
            List<Result> resultList = AnalysisCache.ResultCache.get(key);

            if (resultList != null && resultList.size() > 0) {
                /** 读取方案下指定的素材信息 */
                Material material = schemeService.queryMaterial(userCode, projectCode, schemeCode, materialCode);

                /** 遍历所有被试,取出对应的数据 */

                list = new LinkedList<SchemeResultData>();
                Iterator<Result> iterator = resultList.iterator();
                while (iterator.hasNext()) {
                    // 取出每个被试
                    Result result = iterator.next();
                    SchemeResultData resutlData = new SchemeResultData(result.getResultCode(), result.getResultName());
                    resutlData.setAnswers(result.getAnswers());

                    // 从缓存中读取该被试的注视点统计数据
                    String key_data = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                            + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode
                            + CommonConstants.AnalysisConstats.CACHE_SPLIT + result.getResultCode();
                    List<StatisticFixationData> fixList = AnalysisCache.DataStatisticFixationCache.get(key_data);

                    // 遍历注视点统计信息,取出该素材的统计信息,生成网格数据
                    if (fixList != null && fixList.size() > 0) {
                        List<FixData> gridDataList = new LinkedList<FixData>();
                        for (StatisticFixationData fixData : fixList) {
                            if (fixData.getMaterialCode().equals(material.getCode())) { // 当是该素材的才添加进去
                                FixData data = new FixData(fixData.getPositionX(), fixData.getPositionY(),
                                        fixData.getFixationDuration());
                                data.setFixationStart(fixData.getFixationStart());
                                data.setLeftPupilSize(fixData.getLeftPupilSize());
                                data.setRightPupilSize(fixData.getRightPupilSize());

                                gridDataList.add(data);
                            }
                        }
                        resutlData.setFixData(gridDataList);
                    }

                    list.add(resutlData);
                }

            }

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query grid data error", ex);
            throw new RuntimeException(ex);
        }
        return list;
    }

    /**
     * 查询某个素材的数据(注视点数据和原始数据)
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param materialCode
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<SchemeResultData> queryResultDataByMaterial(String userCode, String projectCode, String schemeCode,
            String materialCode) throws Exception {
        /** 先分别查询出注视点数据和原始数据 */
        List<SchemeResultData> listFixData = this.queryResultFixationDataByMaterial(userCode, projectCode, schemeCode,
                materialCode);
        List<SchemeResultData> listRawData = this.queryResultRawDataByMaterial(userCode, projectCode, schemeCode,
                materialCode);

        /** 再遍历,将相同被试的数据合并 */
        if (listFixData != null && listFixData.size() > 0) {
            for (int i = 0; i < listFixData.size(); i++) {
                for (int j = 0; j <= listRawData.size(); j++) {
                    if (listFixData.get(i).getCode().equals(listRawData.get(j).getCode())) {
                        listFixData.get(i).setData(listRawData.get(j).getData());
                        break;
                    }
                }
            }
        }
        return listFixData;
    }

    /**
     * 查询方案中的所有被试信息
     * 
     * @param userCode
     * @param projectCode
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Result> queryResultList(String userCode, String projectCode, String schemeCode) throws Exception {
        try {
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;
            List<Result> resultList = AnalysisCache.ResultCache.get(key);

            return resultList;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query result list error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个方案中指定的被试信息
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultCode
     * @return
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Result queryResult(String userCode, String projectCode, String schemeCode, String resultCode)
            throws Exception {
        Result result = null;
        try {
            /** 读取方案下所有被试信息 */
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;
            List<Result> resultList = AnalysisCache.ResultCache.get(key);

            if (resultList != null && resultList.size() > 0) {
                Iterator<Result> iterator = resultList.iterator();
                while (iterator.hasNext()) {
                    Result dto = iterator.next();
                    if (dto.getResultCode().equals(resultCode)) {
                        result = dto;
                        break;
                    }
                }
            }
            return result;
        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query result error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个方案中指定的被试信息顺序索引
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultCode
     * @return (从0开始,未查到返回null)
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Integer queryResultIndex(String userCode, String projectCode, String schemeCode, String resultCode)
            throws Exception {
        try {
            /** 读取方案下所有被试信息 */
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;
            List<Result> resultList = AnalysisCache.ResultCache.get(key);

            if (resultList != null && resultList.size() > 0) {
                for (int i = 0; i < resultList.size(); i++) {
                    if (resultList.get(i).getResultCode().equals(resultCode)) {
                        return i;
                    }
                }
            }
            return null;
        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query result index error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 从被试描述文件中读取被试信息
     * 
     * @param key
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Result> readResultList(String key) throws Exception {
        List<Result> resultList = null;

        /** key由usercode、projectcode、schmecode组成 */
        String[] keys = key.split(CommonConstants.AnalysisConstats.CACHE_SPLIT);
        String userCode = keys[0];
        String projectCode = keys[1];
        String schemeCode = keys[2];

        /** 从被试描述文件中读取出来 */
        File file = new File(AnalysisPathUtils.findResultFileName(userCode, projectCode, schemeCode));
        if (!file.exists()) {
            file.createNewFile(); // 对文件为空时做个保护
            resultList = new LinkedList<Result>();
        } else {
            byte[] resultBytes = FileUtils.readFileToByteArray(file);
            if (resultBytes.length > 0) {
                String resultJson = new String(resultBytes, CommonConstants.SysParamConstants.SYS_CHARSET);
                resultList = (List<Result>) JacksonMapper.json2List(resultJson, Result.class);
            } else {
                resultList = new LinkedList<Result>();
            }
        }
        return resultList;
    }

    /**
     * 修改方案中的所有被试信息（问卷信息）
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultList
     *            新的被试信息
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void modifyResultList(String userCode, String projectCode, String schemeCode, List<Result> resultList)
            throws Exception {
        try {
            if (resultList != null && resultList.size() > 0) {
                for (Result result : resultList) {
                    syncResult(null, userCode, projectCode, schemeCode, result, false); // 复用同步结果信息来处理修改,效率不高,因为需要多次刷新文件流到本地磁盘
                }
            }

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("modify result list infomation error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 读取每个被试数据
     * 
     * @param key
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Data> readDataList(String key) throws Exception {

        /** key由usercode、projectcode、schmecode、resultcode组成 */
        String[] keys = key.split(CommonConstants.AnalysisConstats.CACHE_SPLIT);
        String userCode = keys[0];
        String projectCode = keys[1];
        String schemeCode = keys[2];
        String resultCode = keys[3];

        /** 从被试数据文件中读取出来 */
        // 开始遍历原始数据文件
        List<Data> dataList = new LinkedList<Data>(); // 图形分析数据集
        GazeData gaze = null;
        Data data = null;
        BufferedReader rawReader = null;
        try {
            File rawFile = new File(AnalysisPathUtils.findResultRawDataFileName(userCode, projectCode, schemeCode,
                    resultCode));

            rawReader = new BufferedReader(new InputStreamReader(new FileInputStream(rawFile),
                    CommonConstants.SysParamConstants.SYS_CHARSET));
            String line = null;
            while ((line = rawReader.readLine()) != null) {
                gaze = (GazeData) JacksonMapper.json2Bean(line, GazeData.class);

                /** 生成图形分析数据 */
                if (gaze.smoothedCoordinates.x < 0.01 && gaze.smoothedCoordinates.y < 0.01) {
                    // 过滤掉双眼都为0.0的数据（由于是double型，因此需要大于小于比较） 以及为负数的无效数据
                    continue;
                }
                data = new Data(gaze.smoothedCoordinates.x, gaze.smoothedCoordinates.y, gaze.timeStamp);
                dataList.add(data);

                gaze = null;
            }

        } catch (Exception ex) {
            logger.error("", ex);
        } finally {
            if (rawReader != null) {
                try {
                    rawReader.close();
                    rawReader = null;
                } catch (Exception ex) {
                    logger.error("close rawdata file reader error ", ex);
                }
            }
        }

        return dataList;
    }

    /**
     * 同步结果信息
     * 
     * @param userno
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param result
     *            待新增的结果
     * @param addFlag
     *            是否新增标志
     * @throws Exception
     */
    @Override
    public void syncResult(Integer userno, String userCode, String projectCode, String schemeCode, Result result,
            boolean addFlag) throws Exception {
        try {
            /** 再执行实际同步信息 */
            File lockFile = new File(AnalysisPathUtils.findResultFileName(userCode, projectCode, schemeCode)
                    + CommonConstants.AnalysisConstats.SYNC_FILE_LOCK_SUFFIX);
            File srcFile = new File(AnalysisPathUtils.findResultFileName(userCode, projectCode, schemeCode));
            File resultPath = new File(AnalysisPathUtils.findResultDataPath(userCode, projectCode, schemeCode,
                    result.getResultCode()));

            // 定义回调执行流程
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;
            Object[] array = new Object[] {key, result, srcFile, resultPath, addFlag};
            HandlerCaller caller = new HandlerCaller(array) {
                @Override
                public void execute(FileChannel lockChannel) throws Exception {
                    /** 1.分解各个字段 */
                    Object[] array = (Object[]) this.object;
                    String key = (String) array[0];
                    Result result = (Result) array[1];
                    File srcFile = (File) array[2];
                    File resultPath = (File) array[3];
                    boolean addFlag = (Boolean) array[4];

                    /** 2.读取当前结果信息,根据结果编码判断是否新增or修改 */
                    List<Result> resultList = AnalysisCache.ResultCache.get(key);

                    Iterator<Result> iterator = resultList.iterator();
                    Result dto = null;
                    boolean isHave = false;
                    while (iterator.hasNext()) {
                        dto = iterator.next();
                        if (dto.getResultCode().equals(result.getResultCode())) {
                            dto.setResultName(result.getResultName()); // 只更新结果名
                            if (result.getAnswers() != null) {
                                dto.setAnswers(result.getAnswers()); // 若问卷信息不为空,则修改.即是说,不允许将问卷信息从非空->空
                            }
                            isHave = true;
                            break;
                        }
                    }
                    if (!isHave) {
                        resultList.add(result); // 没有修改那么则新增
                    }

                    /** 3.同步的结果信息转换 */
                    String newResultJson = JacksonMapper.list2Json(resultList);
                    ByteBuffer buffer = ByteBuffer.wrap(newResultJson
                            .getBytes(CommonConstants.SysParamConstants.SYS_CHARSET));

                    /** 4.写入并更新缓存 */
                    FileChannel srcChannel = null;
                    try {
                        srcChannel = new FileOutputStream(srcFile).getChannel();
                        srcChannel.write(buffer); // 实际写入描述文件

                        if (!resultPath.exists()) { // 创建方案目录
                            resultPath.mkdir();
                        }

                        AnalysisCache.ResultCache.put(key, resultList); // 更新缓存
                    } catch (Exception ex) {
                        try {
                            // 新增结果时,若出异常后删除结果目录
                            if (addFlag) {
                                if (resultPath.exists()) {
                                    resultPath.delete();
                                }
                            }
                        } catch (Exception e) {
                            logger.error("", e);
                        }
                        throw ex;
                    } finally {
                        if (srcChannel != null) {
                            try {
                                srcChannel.close();
                            } catch (Exception ex) {
                                logger.error("", ex);
                            }
                        }
                    }
                }
            };

            FileLockUtils.handlerByLockFile(lockFile, caller); // 调用文件锁定方式操作
        } catch (Exception ex) {
            logger.error("", ex);
            throw new RuntimeException(ex);
        }

    }

    /**
     * 对同步的结果原始文件做处理. 生成: (1) 各类图形分析数据; (2) 注视点统计数据; (3)扫视点统计数据
     * 
     * @param userno
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultCode
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void syncResultData(Integer userno, String userCode, String projectCode, String schemeCode, String resultCode)
            throws Exception {
        try {

            /** 1.逐行读取原始文件,生成各类处理后的文件 */
            List<Data> dataList = new LinkedList<Data>(); // 图形分析数据集
            GazeData gaze = null;
            Data data = null;

            List<StatisticFixationData> statisticFixationDataList = new LinkedList<StatisticFixationData>(); // 注视点统计数据集
            List<GazeData> alreadyFixStatList = new LinkedList<GazeData>();
            StatisticFixationData statisticFixationData = null;

            List<StatisticSaccadeData> statisticSaccadeDataList = new LinkedList<StatisticSaccadeData>(); // 扫视点统计数据集
            List<GazeData> alreadySacStatList = new LinkedList<GazeData>();
            StatisticSaccadeData statisticSaccadeData = null;

            // 公共使用数据
            TempIndex materialIndexFix = new TempIndex(); // 记录素材索引号,索引会在计算过程中改变,故需使用TempIndex对象封装
            TempIndex materialIndexSac = new TempIndex();
            Long baseTime = null;
            Scheme currentScheme = schemeService.queryScheme(userCode, projectCode, schemeCode);
            Result currentResult = queryResult(userCode, projectCode, schemeCode, resultCode);
            // 获取点击事件
            List<EventModel> eventModels = queryEvent(userCode,projectCode,schemeCode,resultCode);

            // 对素材进行排序
//            Collections.sort(currentScheme.getMaterials(), new Comparator<Material>() {
//                @Override
//                public int compare(Material o1, Material o2) {
//                    return (int)(o1.getOffset() - o2.getOffset());
//                }
//            });

            long offset = 0;
            for (Material material : currentScheme.getMaterials()){
                for (EventModel model : eventModels){
                    if (material.getCode().equalsIgnoreCase(model.getMaterialCode())){
                        material.setDuration(Long.valueOf(model.getIntervalTime()));
                        material.setOffset(offset);
                        break;
                    }
                }
                offset += material.getDuration();
            }
            // 开始遍历原始数据文件
            BufferedReader rawReader = null;
            try {
                File rawFile = new File(AnalysisPathUtils.findResultRawDataFileName(userCode, projectCode, schemeCode,
                        resultCode));

                rawReader = new BufferedReader(new InputStreamReader(new FileInputStream(rawFile),
                        CommonConstants.SysParamConstants.SYS_CHARSET));
                String line = null;
                while ((line = rawReader.readLine()) != null) {
                    gaze = (GazeData) JacksonMapper.json2Bean(line, GazeData.class);
                    if (baseTime == null) {
                        baseTime = gaze.timeStamp; // 基础时间点就是第一个gazedata的上报时间
                    }

                    /** 生成图形分析数据 */
                    data = new Data(gaze.smoothedCoordinates.x, gaze.smoothedCoordinates.y, gaze.timeStamp);
                    dataList.add(data);

                    /** 生成注视点统计数据 */
                    statisticFixationData = statisticService.judgeStatisticFixationData(alreadyFixStatList, gaze,
                            currentScheme, materialIndexFix, baseTime, currentResult, eventModels);
                    if (statisticFixationData != null) {
                        statisticFixationData.setNumber(statisticFixationDataList.size() + 1); // 设置序号
                        statisticFixationDataList.add(statisticFixationData);
                    }
                    statisticFixationData = null;

                    /** 生成扫视点统计数据 */
                    statisticSaccadeData = statisticService.judgeStatisticSaccadeData(alreadySacStatList, gaze,
                            currentScheme, materialIndexSac, baseTime, currentResult, eventModels);
                    if (statisticSaccadeData != null) {
                        statisticSaccadeData.setNumber(statisticSaccadeDataList.size() + 1);
                        statisticSaccadeDataList.add(statisticSaccadeData);
                    }
                    statisticSaccadeData = null;

                    gaze = null;
                }

                if (alreadyFixStatList.size() > 0) { // 当最后一条原始数据读取完后,若待处理的注视点数据集中还有数据,则生成一个新的注视点信息
                    statisticFixationData = statisticService.calcStatisticFixationData(alreadyFixStatList,
                            currentScheme, currentScheme.getMaterials().get(currentScheme.getMaterials().size() - 1),
                            currentResult, baseTime);
                    if (statisticFixationData != null) {
                        statisticFixationData.setNumber(statisticFixationDataList.size() + 1);
                        statisticFixationDataList.add(statisticFixationData);
                    }
                }

                if (alreadySacStatList.size() > 0) { // 当最后一条原始数据读取完后,若待处理的扫视点数据集中还有数据,则生成一个新的扫视点信息
                    statisticSaccadeData = statisticService.calcStatisticSaccadeData(alreadySacStatList, currentScheme,
                            currentScheme.getMaterials().get(currentScheme.getMaterials().size() - 1), currentResult,
                            baseTime);
                    if (statisticSaccadeData != null) {
                        statisticSaccadeData.setNumber(statisticSaccadeDataList.size() + 1);
                        statisticSaccadeDataList.add(statisticSaccadeData);
                    }
                }

            } catch (Exception ex) {
                logger.error("", ex);
                throw new ResultServiceException(ResultServiceException.RESULT_FILE_DEFINED_ERROR);
            } finally {
                if (rawReader != null) {
                    try {
                        rawReader.close();
                        rawReader = null;
                    } catch (Exception ex) {
                        logger.error("close rawdata file reader error ", ex);
                    }
                }
            }

            /** 2.写入图形分析数据 */
            File processedFile = new File(AnalysisPathUtils.findResultDataFileName(userCode, projectCode, schemeCode,
                    resultCode));
            // 定义回调执行流程
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + resultCode;
            Object[] array = new Object[] {key, dataList};
            HandlerCaller caller = new HandlerCaller(array) {
                @Override
                public void execute(FileChannel lockChannel) throws Exception {
                    /** 2.1.分解各个字段 */
                    Object[] array = (Object[]) this.object;
                    String key = (String) array[0];
                    List<Data> dataList = (List<Data>) array[1];

                    /** 2.2.写入处理后的数据 */
                    String newResultJson = JacksonMapper.list2Json(dataList);
                    ByteBuffer buffer = ByteBuffer.wrap(newResultJson
                            .getBytes(CommonConstants.SysParamConstants.SYS_CHARSET));
                    lockChannel.write(buffer); // 拿到的本来就是processedFile

                    /** 2.3.更新缓存 */
                    AnalysisCache.DataCache.put(key, dataList);
                }
            };
            FileLockUtils.handlerByLockFile(processedFile, caller); // 调用文件锁定方式操作

            /** 3.写入注视点统计数据 */
            File statisticFixationFile = new File(AnalysisPathUtils.findResultDataStatisticFixationFileName(userCode,
                    projectCode, schemeCode, resultCode));
            // 定义回调执行流程
            String keyFixation = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + resultCode;
            Object[] arrayFixation = new Object[] {keyFixation, statisticFixationDataList};
            HandlerCaller callerFixation = new HandlerCaller(arrayFixation) {
                @Override
                public void execute(FileChannel lockChannel) throws Exception {
                    /** 3.1.分解各个字段 */
                    Object[] array = (Object[]) this.object;
                    String key = (String) array[0];
                    List<StatisticFixationData> statisticFixationDataList = (List<StatisticFixationData>) array[1];

                    /** 3.2.写入注视点数据 */
                    String newResultJson = JacksonMapper.list2Json(statisticFixationDataList);
                    ByteBuffer buffer = ByteBuffer.wrap(newResultJson
                            .getBytes(CommonConstants.SysParamConstants.SYS_CHARSET));
                    lockChannel.write(buffer); // 拿到的本来就是fixationFile

                    /** 3.3.更新缓存 */
                    AnalysisCache.DataStatisticFixationCache.put(key, statisticFixationDataList);
                }
            };
            FileLockUtils.handlerByLockFile(statisticFixationFile, callerFixation); // 调用文件锁定方式操作

            /** 4.写入扫视点统计数据 */
            File statisticSaccadeFile = new File(AnalysisPathUtils.findResultDataStatisticSaccadeFileName(userCode,
                    projectCode, schemeCode, resultCode));
            // 定义回调执行流程
            String keySaccade = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + resultCode;
            Object[] arraySaccade = new Object[] {keySaccade, statisticSaccadeDataList};
            HandlerCaller callerSaccade = new HandlerCaller(arraySaccade) {
                @Override
                public void execute(FileChannel lockChannel) throws Exception {
                    /** 4.1.分解各个字段 */
                    Object[] array = (Object[]) this.object;
                    String key = (String) array[0];
                    List<StatisticSaccadeData> statisticSaccadeDataList = (List<StatisticSaccadeData>) array[1];

                    /** 4.2.写入扫视点数据 */
                    String newResultJson = JacksonMapper.list2Json(statisticSaccadeDataList);
                    ByteBuffer buffer = ByteBuffer.wrap(newResultJson
                            .getBytes(CommonConstants.SysParamConstants.SYS_CHARSET));
                    lockChannel.write(buffer); // 拿到的本来就是saccadeFile

                    /** 4.3.更新缓存 */
                    AnalysisCache.DataStatisticSaccadeCache.put(key, statisticSaccadeDataList);
                }
            };
            FileLockUtils.handlerByLockFile(statisticSaccadeFile, callerSaccade); // 调用文件锁定方式操作

        } catch (ResultServiceException ex) {
            throw ex;
        } catch (Exception ex) {
            logger.error("", ex);
            throw new RuntimeException(ex);
        }
    }

    private List<EventModel> queryEvent(String userCode, String projectCode, String schemeCode, String resultCode) {
        List<EventModel> list = new ArrayList<EventModel>();
        String key = AnalysisPathUtils.findResultDataPath(userCode,projectCode,schemeCode,resultCode)+CommonConstants.SysParamConstants.FILE_SEPARATOR+"EventInfo.json";
        try {
            InputStreamReader fileReader = new InputStreamReader(new FileInputStream(key),"utf-8");
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String str = null;
            while ((str = bufferedReader.readLine()) != null){
                EventModel eventModel = (EventModel) JacksonMapper.json2Bean(str,EventModel.class);
                list.add(eventModel);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (list.isEmpty()){
            logger.error("没有加载到EventInfo.json");
        }
        return list;
    }

    /**
     * 计算所有结果的属性详情描述
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param resultList
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void calcResultAttribute(String userCode, String projectCode, String schemeCode, List<Result> resultList)
            throws Exception {
        if (resultList != null && resultList.size() > 0) {
            Iterator<Result> iterator = resultList.iterator();
            while (iterator.hasNext()) {
                calcResultAttribute(userCode, projectCode, schemeCode, iterator.next()); // 调用单个计算
            }
        }
    }

    /**
     * 计算某个结果的属性详情描述
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param result
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void calcResultAttribute(String userCode, String projectCode, String schemeCode, Result result)
            throws Exception {
        if (result.getAnswers() != null && result.getAnswers().size() > 0) {
            String tooltip = "";
            Question attribute = null;
            Answer answer = null;
            for (int i = 0; i < result.getAnswers().size(); i++) { // 获取该结果的所有答案进行遍历
                answer = result.getAnswers().get(i);
                attribute = schemeService.queryResultAttribute(userCode, projectCode, schemeCode, answer.getNo());
                if (attribute == null) { // 由于结果中的answer有可能是问卷,因此当未查询到时,直接进行下一个
                    continue;
                }

                tooltip += attribute.getName() + ":";
                if (attribute.getShowType() == Question.SHOW_TYPE.Input.getCode()) { // input类型时直接展示
                    tooltip += answer.getValue();
                } else if (attribute.getShowType() == Question.SHOW_TYPE.List.getCode()) { // List类型时需要翻译具体的Key值
                    for (int j = 0; j < attribute.getOptionList().size(); j++) {
                        if (attribute.getOptionList().get(j).getKey().equals(answer.getValue())) {
                            tooltip += attribute.getOptionList().get(j).getValue();
                            break;
                        }
                    }
                }
                tooltip += " ";
            }
            result.setAttributeDescription(tooltip);
        }
    }
}
